<?php
namespace app\services;

use app\utils\Functions as F;
use app\utils\GEO as geo;
use app\services\ModifyService;
use app\models\ResourceAppAdspaceMap as Raamap;
use app\models\ResourceApp;
use app\models\ResourcePack;

class SolutionService extends BaseService
{
    // condition id 和对应 model 的映射表
    const NOLIMIT = -1;

    protected $conditionIdNameMaps = [];
    protected $videoConditionIdNameMaps = [];
    protected $mobileConditionIdNameMaps = [];

    public function init() {
        parent::init();
        $this->initConditionMaps();
    }

    protected function initConditionMaps() {
        $conditions = $this->getAll('ConditionType', ['status'=>1]);
        foreach ($conditions as $id => $condition) {
            $model = $this->modelize($condition['table_name']);
            $condition['is_for_display'] && $this->conditionIdNameMaps[$model] = $id;
            $condition['is_for_video'] && $this->videoConditionIdNameMaps[$model] = $id;
            $condition['is_for_mobile'] && $this->mobileConditionIdNameMaps[$model] = $id;
        }
    }

    public $conditionKeys = [
        'ConditionAdspaceCategory'      => ['adspace_category_id'],
        'ConditionIp'                   => ['start_ip', 'end_ip'],
        'ConditionZone'                 => ['zone_id'],
        'ConditionInstrument'           => ['instrument_category_id'],
        'ConditionOs'                   => [],
        'ConditionBrowser'              => [],
        'ConditionLanguage'             => ['code'],
        'ConditionAdspace'              => ['adspace_id'],
        'ConditionBrand'                => ['brand_safe'],
        'ConditionFrequency'            => ['mode', 'frequency_type_id', 'frequency'],
        'ConditionRetarget'             => ['retarget_type_id', 'retarget_site_id'],
        'ConditionWeather'              => ['weather_type_id'],
        'ConditionAudience'             => ['audience_client_id', 'audience_type_id'],
        'ConditionMobileAdspaceType'    => ['adspace_type'],
        'ConditionMobileBrand'          => ['brand'],
        'ConditionMobileNetwork'        => ['network'],
        'ConditionMobileCarrier'        => ['carrier'],
        'ConditionMobileOs'             => ['os'],
        'ConditionMobileOsVersion'      => ['os_version'],
        'ConditionSchedule'             => ['schedule'],
        'ConditionAdspaceAdnetwork'     => ['adnetwork'],
        'ConditionChannel'              => ['channel_id_list'],
        'ConditionUrl'                  => ['urls'],
    ];

    public $matrix = [
        'id'            => 'id',
        'name'          => 'name',
        'launch'        => 'campaign',
        'status'        => ['status', '$v==1 && $v="waiting";
                                       $v==2 && $v="effective";
                                       $v==3 && $v="paused";
                                       $v==4 && $v="deleted";
                                       $v==5 && $v="overdue";
                                       $v==6 && $v="closed";
                                       $v==7 && $v="incomplete";
                                       $v==8 && $v="incomplete";
                                       $v==9 && $v="incomplete";
                                       return $v;'],
        'launchTime'    => 'range_date',
        'creativeCount' => 'creativeCount',
        'dailyBudget'   => 'daily_budget',
        'type'          => 'type',
        'totalBudget'   => 'budget',
        'cost'          => ['cost', 'return number_format($v, 2);'],
        'totalCost'     => ['totalCost', 'return number_format($v, 2);'],
        'showCount'     => 'shows',
        'clickCount'    => 'clicks',
        'clickRate'     => ['clickRate', 'return number_format($v, 2);'],
        'cpm'           => ['cpm', 'return number_format($v, 2);'],
        'cpc'           => ['cpc', 'return number_format($v, 2);'],
        'modifiedDate'  => 'last_update',
        'operate'       => ['id', 'return  "edit, view";'],
        'audit'         => ['solution_audit', '$v==0 && $v="off";
                                               $v==1 && $v="on";
                                               return $v;'],
        'dailyRate'     => 'daily_rate',
        'totalRate'     => 'total_rate',  
    ];

    public $auditMatrix = [
        'id'            => 'id',
        'name'          => 'name',
        'launch'        => 'campaign',
        'status'        => ['status', '$v==1 && $v="waiting";
                                       $v==2 && $v="effective";
                                       $v==3 && $v="paused";
                                       $v==4 && $v="deleted";
                                       $v==5 && $v="overdue";
                                       $v==6 && $v="closed";
                                       $v==7 && $v="incomplete";
                                       $v==8 && $v="incomplete";
                                       $v==9 && $v="incomplete";
                                       return $v;'],
        'launchTime'    => 'range_date',
        'oldBudget'     => 'old_budget',
        'totalBudget'   => 'budget',
        'modifiedDate'  => 'last_update',
        'operate'       => ['id', 'return "audit";'],
        'editStatus'   => ['old_budget', 'if($v==0) return \Yii::t("common", "Create");
                                            else return \Yii::t("common", "Edit");' ],
    ];

    /**
     * v1:
     *    计算solution剩余生命天数
     *    需要同solution的投放 weekday 进行交集
     * v2:
     *    TODO 计算为剩余生命小时
     */
    public function calculateSolutionLastRanges($solutionId) {
        $solution = $this->getOne('Solution', ['id' => $solutionId]);
        $start = strtotime($solution['start_date']);
        $end = strtotime($solution['end_date']);
        $today = strtotime(date('Y-m-d'));
        //已经结束
        if ($today > $end) return 0;
        $schedule = $this->getOne('ConditionSchedule', ['solution_id' => $solutionId, 'status' => 1]);
        $schedule = $schedule['schedule'];
        $workingdays = explode(',', $solution['weekday_slot']);
        $begin = $start;
        if ($today >= $start) $begin = $today;
        $counter = 0;
        while ($begin <= $end) {
            $weekday = date('w', $begin);
            if (in_array($weekday, $workingdays)) {
                $counter += 1;
            }
            $begin += 24*60*60;
        }
        return $counter;
    }

    /**
     * 新建策略时计算总时长和已燃烧时长
     */
    public function calculateSolutionLifeSpanHour($solution) {
        date_default_timezone_set('Asia/Shanghai');
        if (is_numeric($solution)) {
            $solution = $this->getOne('Solution', ['id' => $solution]);
        }
        $pauseHours = $this->getSolutionPauseHour($solution['id']);
        $workingdays = explode(',', $solution['weekday_slot']);
        $beg = strtotime($solution['start_date']);
        $end = strtotime($solution['end_date']);
        $today = strtotime(date('Y-m-d'));
        $lifeHours = [];
        $burnHours = [];
        // 当前时间决定剩余存活时间
        $nowHour = date('YmdH', time());
        while ($beg <= $end) {
            $weekday = date('w', $beg);
            if (in_array($weekday, $workingdays)) {
                for ($hour = 0; $hour < 24; $hour++) {
                    if (in_array($weekday*100+$hour, $pauseHours)) continue;
                    $liveHour = date('Ymd00', $beg)+$hour;
                    $lifeHours[] = $liveHour;
                    if ($liveHour <= $nowHour) {
                        $burnHours[] = $liveHour; 
                    }
                }
            }
            $beg += 24*60*60;
        }
        return [count($lifeHours), count($burnHours)]; 
    }

    //编辑策略时计算有效时长和当前小时是否有效
    public function calculateSolutionHours($solution, $nowHour, $type = 'valid') {
        date_default_timezone_set('Asia/Shanghai');
        if (is_numeric($solution)) {
            $solution = $this->getOne('Solution', ['id' => $solution]);
        }
        $pauseHours = $this->getSolutionPauseHour($solution['id']);
        $workingdays = explode(',', $solution['weekday_slot']);
        $beg = strtotime($solution['start_date']);
        $end = strtotime($solution['end_date']);
        $today = strtotime(date('Y-m-d'));
        $valid = false;
        $validHours = [];
        // 当前时间决定剩余存活时间
        while ($beg <= $end) {
            $weekday = date('w', $beg);
            if (in_array($weekday, $workingdays)) {
                for ($hour = 0; $hour < 24; $hour++) {
                    if (in_array($weekday*100+$hour, $pauseHours)) continue;
                    $liveHour = date('Ymd00', $beg)+$hour;
                    if ($liveHour == $nowHour) $valid = true;
                    if ($type == 'valid' && ($liveHour > $nowHour)) {
                        $validHours[] = $liveHour;
                    }
                    if($type == 'invalid' && ($liveHour < $nowHour)) {
                        $validHours[] = $liveHour;
                    }
                }
            }
            $beg += 24*60*60;
        }
        return [count($validHours), $valid];
    }

    public function isCurrenHourMasked($solutionId) {
        $pauseHours = $this->getSolutionPauseHour($solutionId);
        $currentHour = date('H', time());
        $weekday = date('w', time());
        $current = $weekday.'00'+$currentHour;
        return in_array($current, $pauseHours);
    }

    public function getSolutionTodayLeftHours($solutionId) {
        $solution = $this->M('Solution', $solutionId);
        if (strtotime($solution['start_date']) > strtotime(date('Y-m-d'))) return 0;
        if (strtotime($solution['end_date']) < strtotime(date('Y-m-d'))) return 0;
        $pauseHours = $this->getSolutionPauseHour($solutionId);
        $currentHour = date('H', time());
        $weekday = date('w', time());
        $left = 0;
        for ($i = $currentHour; $i < 24; $i++) {
            $current = $weekday.'00'+$i;
            if (!in_array($current, $pauseHours)) $left++;
        }
        return $left;
    }

    public function getSolutionPauseHour($solutionId) {
        //$scheduleType = $this->M('ConditionType', ['table_name' => 'condition_schedule']);
        //$scheduleType = $scheduleType[id];
        $scheduleType = 20;
        $scheduleCondition = $this->getOne('Condition', ['solution_id' => $solutionId, 'type_id' => $scheduleType]);
        $pauseHours = [];
        if ($scheduleCondition['use_whitelist'] == 0) {
            $schedule = $this->getOne('ConditionSchedule', ['solution_id' => $solutionId, 'is_whitelist' => 0, 'status' => 1]);
            if ($schedule) {
                $pauseHours = $schedule['schedule'];
                if ($pauseHours) $pauseHours = explode(',', $pauseHours);
            }
        }
        !$pauseHours && $pauseHours = [];
        return $pauseHours;
    }

    /**
     * 初始化总预算表
     */
    public function initBalance($solutionId, $change = false) {
        $solution = $this->M('Solution', $solutionId);
        $solutionBudget = $solution['budget'];
        $balance = $this->getOne('SolutionBalance', ['solution_id' => $solutionId]);
        $beforeModify = $balance;
        $flag = false;
        if ($balance && $change) {
            $flag = true;
            // list($validHours, $valid) = $this->calculateSolutionValidSpanHour($solution);
            $nowHour = date('YmdH', time());
            list($validHours, $valid) = $this->calculateSolutionHours($solution, $nowHour, $type = 'valid');
            $balance['burn_hours'] = 0;
            $balance['hours'] = $validHours;
            if ($valid) {
                $balance['burn_hours'] = 1;
                $balance['hours'] = $validHours + 1;
            }
            $balance['cost_before_edit'] = $balance['cost_from_log'] + $balance['cost_today'];
            $balance['is_edited'] = 1;
            unset($balance['id']);
            unset($balance['last_update']);
            unset($balance['cost_from_log']);
            unset($balance['cost_yesterday']);
            unset($balance['cost_today']);
        } else {              //new solution
            list($hours, $burnHours) = $this->calculateSolutionLifeSpanHour($solution);
            $balance['balance'] = 0;
            if ($hours <= 0) {
                $currentHourBalance = 0;
            } else {
                $currentHourBalance = round($solutionBudget/$hours, 2);
            }
            $balance['balance'] = $currentHourBalance;
            $balance['hours'] = $hours;
            $balance['burn_hours'] = $burnHours;
            $balance['solution_id'] = $solutionId;
        }    
        $save = $this->saveOn('SolutionBalance', ['solution_id' => $solutionId], $balance);
        if($flag && $save){
            $modifyService = new ModifyService();
            $modifyService -> saveModify($beforeModify,$save,'SolutionBalance',['id'],['last_update']);
        }
        return $save;
    }

    public function getTopInstrument() {
        $ad_network_id = $this->S('ad_network_id');
        !$ad_network_id && $ad_network_id = 1;
        $where = " ad_network_id = $ad_network_id and parent_category_id is null ";
        $topInstruments = $this->getAll('Instrument', $where);
        //暂无 SMART_TV 定向
        array_pop($topInstruments);
        return $topInstruments;
    }

    /**
     * 创建基本定向内容时需要占位定向id, 确保每个投放的定向个数一致
     */
    public function takeUpConditionTypes($solutionId, $type='is_for_mobile') {
        $types = $this->getAll('ConditionType', ['status'=>1, "$type"=>1]);
        foreach ($types as $type) {
            $typeData = ['solution_id' => $solutionId, 'type_id' => $type['id']];
            $this->checkAndSave('Condition', $typeData); 
        }
    }

    public function saveConditions($solutionId, $type='mobile') {
        $this->saveConditionIp();
        $this->saveConditionZone();
        $this->saveConditionLanguage();
        $this->saveConditionFrequency($solutionId);
        $this->saveConditionWeather();
        $this->saveConditionSchedule($solutionId);
        $this->saveConditionAudience($solutionId);
        if ($type == 'mobile') {
            $this->saveConditionMobileNetwork($solutionId);
            $this->saveConditionMobileCarrier($solutionId);
            $this->saveConditionMobileOs($solutionId);
            $this->saveConditionMobileOsVersion($solutionId);
            $this->saveConditionMobileBrand($solutionId);
            $this->saveConditionMobileAdspaceType($solutionId);
        } else {
            $this->saveConditionAdspaceCategory($solutionId);
            $this->saveConditionInstrument($solutionId);
            $this->saveConditionBrandSafe($solutionId);
            $this->saveConditionRetarget($solutionId);
            $this->saveConditionChannel($solutionId);
            $this->saveConditionUrl($solutionId);
        }
        $this->updateSolutionTime($solutionId);
    }

    public function saveConditionAdspaceAdnetwork($solutionId) { 
        $model = 'ConditionAdspaceAdnetwork';
        $this->update($model, ['solution_id' => $solutionId]);
        $status = 'adspace_black_white_radio';
        $status = $this->getStatus($status);
        $this->updateConditionBWListType($solutionId, $model, $status);
        if($status == self::NOLIMIT)
            return true;
        $solution = $this->getOne('Solution', ['id' => $solutionId]);
        if ($solution['is_pmp'] == 1) {
            $adnetworks = [1];
            $status = 2;
        } else {
            $adnetworks = $this->I('ad_network');
            !$adnetworks && $adnetworks = [1];
            if ($this->I('adspace_check') == 'part') {
                list($adnetworks, $adspaces) = $this->checkAdspaces($adnetworks, $this->I('adspace_type'));
                $status = 2;
            }
        }

        foreach ($adnetworks as $adnetwork) {
            $condition = ['is_whitelist' => $status, 'adnetwork' => $adnetwork];
            $this->save($model, $condition);
        }
    }

    public function checkAdspaces($adNets, $adspaces) {
        $adnetworks = [];
        $ads = [];
        if ($adspaces) {
            foreach ($adspaces as $key => $id) {
                $data = explode('_', $id);
                if(in_array($data[0], $adNets)) {
                    $adnetworks[] = $data[0];
                    $ads[$key] = $data[1];
                }
            }
        }
        $adnetworks = array_intersect($adnetworks, $adNets);
        return [$adnetworks, $ads];
    }

    public function saveConditionFrequency($solutionId) {
        $this->update('ConditionFrequency', ['solution_id' => $solutionId]);
        $mode = $this->I('frequency_mode');
        $bw = 1;
        if ($mode == 0) $bw = 0;
        $this->updateConditionBWListType($solutionId, 'ConditionFrequency', $bw);
        if ($mode) {
            $condition = ['is_whitelist' => 1];
            $type = $this->I('frequency_ip_frequency_id');
            $no = $this->I('frequency_ip_no');
            if ($mode == 1) {
                $type = $this->I('frequency_equipment_frequency_id');
                $no = $this->I('frequency_equipment_no');
            } else if ($mode == 3) {
                $type = $this->I('frequency_person_frequency_id');
                $no = $this->I('frequency_person_no');
            }
            $condition['mode'] = $mode; 
            $condition['frequency_type_id'] = $type;
            $condition['frequency'] = $no;
            $this->save('ConditionFrequency', $condition);
        }
    }

    public function saveConditionMobileAdspaceType($solutionId) {
        $this->update('ConditionMobileAdspaceType', ['solution_id' => $solutionId]);
        $bw = $this->I('content_black_white_radio');
        $this->updateConditionBWListType($solutionId, 'ConditionMobileAdspaceType', $bw);
        $mat = $this->I('content_type');
        if ($mat) {
            $mat = implode(',', $mat);
            $condition = ['adspace_type' => $mat, 'solution_id' => $solutionId, 'is_whitelist' => $bw];
            return $this->save('ConditionMobileAdspaceType', $condition);
        }
    }

    public function saveConditionMobileBrand($solutionId) {
        $this->update('ConditionMobileBrand', ['solution_id' => $solutionId]);
        $brand = $this->I('mobile_brand');
        $bw = $this->I('mobile_brand_black_white_radio');
        if ($this->I('mobile_brand_condition') == 'target_detail') {
            $condition = ['brand' => $brand, 'solution_id' => $solutionId];
            $this->save('ConditionMobileBrand', $condition);
        } else {
            $bw = self::NOLIMIT;
        }
        $this->updateConditionBWListType($solutionId, 'ConditionMobileBrand', $bw);
    }

    public function saveConditionMobileOs($solutionId) {
        $this->update('ConditionMobileOs', ['solution_id' => $solutionId]);
        $os = $this->I('mobile_os');
        $bw = $this->I('mobile_os_black_white_radio');
        if ($this->I('mobile_os_condition') == 'target_detail') {
            $condition = ['os' => $os, 'solution_id' => $solutionId];
            $this->save('ConditionMobileOs', $condition);
        } else {
            $bw = self::NOLIMIT;
        }
        $this->updateConditionBWListType($solutionId, 'ConditionMobileOs', $bw);
    }

    public function saveConditionMobileOsVersion($solutionId) {
        $this->update('ConditionMobileOsVersion', ['solution_id' => $solutionId]);
        $osv = $this->I('mobile_os_version');
        $bw = $this->I('mobile_os_version_black_white_radio');
        if ($this->I('mobile_os_version_condition') == 'target_detail') {
            $condition = ['os_version' => $osv, 'solution_id' => $solutionId];
            $this->save('ConditionMobileOsVersion', $condition);
        } else {
            $bw = self::NOLIMIT;
        }
        $this->updateConditionBWListType($solutionId, 'ConditionMobileOsVersion', $bw);
    }

    public function saveConditionMobileCarrier($solutionId) {
        $carrier = $this->I('mobile_carrier');
        $condition = ['carrier' => $carrier, 'solution_id' => $solutionId];
        $this->update('ConditionMobileCarrier', ['solution_id' => $solutionId]);
        $this->save('ConditionMobileCarrier', $condition);
        $this->updateConditionBWListType($solutionId, 'ConditionMobileCarrier', 1);
    }

    public function saveConditionMobileNetwork($solutionId) {
        $network = $this->I('mobile_network');
        $condition = ['network'     => $network, 'solution_id' => $solutionId];
        $this->update('ConditionMobileNetwork', ['solution_id' => $solutionId]);
        $this->save('ConditionMobileNetwork', $condition);
        $this->updateConditionBWListType($solutionId, 'ConditionMobileNetwork', 1);
    }

    public function saveConditionSchedule($solutionId) { 
        $period = $this->I('period');
        $model = 'ConditionSchedule';
        $oldSchedule = $this->getOne($model, ['solution_id' => $solutionId, 'status' => 1]);
        $this->update($model, ['solution_id' => $solutionId]);
        if ($period) {
            $weekday_slot = [];
            foreach ($period as $time) {
                $week = substr($time, 0, 1);
                $hour = substr($time, -2);
                $weekday_slot[$week][] = $hour;
            }
            $weeks = [];
            foreach ($weekday_slot as $week => $hours) {
                if (count($hours) == 24) {
                    $weeks[] = $week;
                }
            }
            $weeks = array_unique($weeks);
            sort($weeks);
            $dummyWeeks = [0,1,2,3,4,5,6];
            $solution = $this->M('Solution', $solutionId);
            if ($weeks) {
                if (in_array(7, $weeks)) $weeks[] = 0;
                $weeks = array_diff($dummyWeeks, $weeks);
                $solution['weekday_slot'] = implode(',', $weeks);
            } else {
                $solution['weekday_slot'] = implode(',', $dummyWeeks);
            }
            $this->save('Solution', $solution);
            $period = array_unique($period);
            sort($period);
            $schedule['solution_id'] = $solutionId;
            $schedule['is_whitelist'] = 0;
            $schedule['schedule'] = implode(',', $period);
            $this->save($model, $schedule);
            $this->updateConditionBWListType($solutionId, $model, 0);
        } else {
            $solution = $this->M('Solution', $solutionId);
            $solution['weekday_slot'] = '0,1,2,3,4,5,6';
            $this->save('Solution', $solution);
        }
        // 初始化 solution balance table
        $newSchedule = $this->getOne($model, ['solution_id' => $solutionId, 'status' => 1]);
        $oldPeriod = '';
        if ($oldSchedule) $oldPeriod = $oldSchedule['schedule'];
        $newPeriod = '';
        if ($newSchedule) $newPeriod = $newSchedule['schedule'];
        if ($newPeriod != $oldPeriod) {
            $this->initBalance($solutionId, true);
        }
        // $this->initBalance($solutionId);
    }

    public function saveConditionAdspaceCategory($solutionId) {
        $model = 'ConditionAdspaceCategory';
        $this->update($model, ['solution_id' => $solutionId]);
        $bw = $this->I('content_black_white_radio');
        $this->updateConditionBWListType($solutionId, $model, $bw);
        $mat = $this->I('content_type');
        if ($mat) {
            $mat = implode(',', $mat);
            $condition = ['adspace_category_id' => $mat, 'solution_id' => $solutionId];
            $this->save($model, $condition);
        }
        $this->saveConditionType($model, 'content', 'content_type');
    }

    public function saveConditionLanguage() {
        $this->saveConditionType('ConditionLanguage', 'lang', 'lang_language_radio');
    }

    public function getIps() {
        $ips = $this->I('condition_ips');
        if (empty($ips) || !$ips[0]) return []; 
        $ipsList = trim(array_shift($ips), ',');
        $ips = explode(',', $ipsList);
        $ip2longs = [];
        foreach ($ips as $ip) {
            list($beg, $end) = explode(' - ', $ip);
            $ip2longs[] = ['start_ip' => ip2long($beg), 'end_ip' => ip2long($end)];
        }
        return $ip2longs;
    }

    public function getAdspaces($adNetworkId=1, $class=1) {
        $on = "ad_network_id='$adNetworkId'";
        if (!$adNetworkId) {
            $on = '';
        }
        if (!in_array(0, $this->s('media_slot'))) {
            $ids = implode(',', $this->s('media_slot'));   
            $medias = $this->getAll('AdnetworkMedia', "id in ($ids)");
            $medias = array_column($medias, 'media_id');
            $mediaIds = array_values($medias);
            if ($mediaIds) {
                $adspaceMediaIds = implode(',', $mediaIds);
                if ($on) $on .= ' and ';
                $on .= " media_id in ($adspaceMediaIds) ";
            }
        }
        $on && $on .= ' and status = 0 ';
        !$on && $on .= ' status = 0 ';
        $adspaces = [];
        $vadspaces = [];
        $madspaces = [];
        $mvadspaces = [];
        if ($class == 1) {
            $adspaces = $this->getAll('Adspace', $on);
            $vadspaces = $this->getAll('VideoAdspace', $on);
        }
        if ($class == 2) {
            $madspaces = $this->getAll('MobileAdspace', $on);
            $mvadspaces = $this->getAll('MobileVideoAdspace', $on);
        }

        $ret = [];
        if (in_array(1, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $adspaces);
        }
        if (in_array(2, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $vadspaces); 
        }
        if (in_array(3, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $madspaces);
        }
        if (in_array(4, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $mvadspaces);
        }
        return $ret;
    }

    public function saveConditionInstrument() {
        /*
        $model = 'ConditionAdspaceCategory';
        $this->update($model, ['solution_id' => $solutionId]);
        $bw = $this->I('content_black_white_radio');
        $this->updateConditionBWListType($solutionId, $model, $bw);
        $mat = $this->I('content_type');
        if ($mat) {
            $mat = implode(',', $mat);
            $condition = ['adspace_category_id' => $mat, 'solution_id' => $solutionId];
            $this->save($model, $condition);
        }
        $this->saveConditionType($model, 'content', 'content_type');
        */

        $instruments = $this->getInstruments();
        $this->saveConditionType('ConditionInstrument', 'system', 'instrument_devices', $instruments);
    }

    // 第一版只定向大类
    public function getInstruments() {
        return $instruments = $this->I('instrument_devices');
        if (empty($instruments)) {
            $instruments = [];
            $subIds = [];
        }
        $all = $this->getAll('Instrument', ['ad_network_id'=>1]);
        foreach ($instruments as $instrument) {
            $sets = $this->getAll('Instrument', ['description'=>$instrument]);
            //存储自增 id, 目前只定向大类
            foreach ($sets as $set) {
                $parentId = $set['parent_category_id'];
                if (is_null($parentId)) {
                    $subIds[] = $set['id'];
                    continue;
                }
                $parent = $this->getAll('Instrument', ['category_id' => $parentId]);
                $cates = array_pop($parent);
                $subIds[] = $cates['id'];
            }
        }
        return $subIds;
    }

    public function getFrequencyTypes() {
        return $this->getAll('ConditionFrequencyType');
    }

    public function getWeatherTypes() {
        $weather = $this->getAll('WeatherType');
        $weather = $this->sortLevel($weather, 'name', ['pid', 'id']);
        return $weather;
    }

    public function getAudienceTypes() {
        $audienceTypes = $this->getAll('AudienceType');
        $audienceTypes = $this->sortLevel($audienceTypes, 'name', ['pid', 'id', 'name',]);
        return $audienceTypes;
    }

    public function getRetargetTypes() {
        $retarget = $this->getAll('RetargetType', ['status' => 1]);
        return $retarget;
    }

    public function getAppIdsOnAdspaceId(array $adIds)
    {
        $on = ['status' => 1, 'adspace_id' => $adIds];
        $apps = Raamap::find()->select(['concat("app_", app_id) capp_id', 'app_id'])
            ->where($on)->asArray()->all();
        return $apps;
        //return array_column($apps, 'app_id');
    }

    public function getAdspaceIdsOnAppId()
    {
        $apps = $this->I('pack_list');
        $apps = explode(',', $apps);
        $adIds = [];
        if ($apps) {
            $on = "concat('app_', app_id) in ('" . implode("','", $apps) . "')";
            $adIds = Raamap::find()->select(['adspace_id adId'])
                                   ->where($on)->asArray()->all();
            $adIds = array_column($adIds, 'adId');
        }
        $this->I('adspace_type', $adIds);
        return $adIds;
    }

    public function saveConditionAdspace($solutionId) {
        $adspaces = $this->getAdspaceIdsOnAppId();
        $this->update('ConditionAdspace', ['solution_id' => $solutionId]);
        $solution = $this->getOne('Solution', ['id' => $solutionId]);
        if ($solution['is_pmp'] == 1) {
            $this->saveConditionType('ConditionAdspace', 'adspace', 'adspace_type', $this->I('adspace_type'));
        } else {
            $adnetworks = $this->I('ad_network');
            !$adnetworks && $adnetworks = [1];
            //list($adnetworks, $adspaces) = $this->checkAdspaces($adnetworks, $this->I('adspace_type'));
            $this->saveConditionType('ConditionAdspace', 'adspace', 'adspace_type', $adspaces);
        }
    }

    /**
     * get targeted weather types
     *
     */
    public function getWeatherTypeIds() {
        $weatherTypeIds = [];
        $in = $this->I();
        $weatherTemperature = [];
        $weatherHumidity = [];
        $weatherAqi = [];
        if ($in['weather_temperature_black_white_radio']) {
            $weatherTemperature = $in['weather_temperature'];
        }
        if ($in['weather_humidity_black_white_radio']) {
            $weatherHumidity = $in['weather_humidity'];
        }
        if ($in['weather_aqi_black_white_radio']) {
            $weatherAqi = $in['weather_aqi'];
        }
        $weatherTypeIds[] = implode(',', $weatherTemperature);
        $weatherTypeIds[] = implode(',', $weatherHumidity);
        $weatherTypeIds[] = implode(',', $weatherAqi);
        return implode(';', $weatherTypeIds);
    }

    /**
     * save weather target condition
     */
    public function saveConditionWeather() {
        $weatherTypeIds = $this->getWeatherTypeIds();
        $model = 'ConditionWeather';
        $solutionId = $this->S('solution_id');
        //default is blacklist
        $bw = 0; 
        if ($weatherTypeIds != ';;') $bw = 1;
        $this->updateConditionBWListType($solutionId, $model, $bw);
        // set all old condition as invalid
        $where = ['solution_id' => $solutionId, 
                  'is_whitelist' => $bw];
        $this->update($model, $where);
        $data = array_merge($where, ['weather_type_id' => $weatherTypeIds]);
        return $this->save($model, $data);
    }

    public function saveConditionAudience($solutionId) {
        $model = 'ConditionAudience';
        !$solutionId && $solutionId = $this->S('solution_id');
        $audienceClass = $this->I('audience_class');
        $audienceIds = $this->I('audience_category');
        if (isset($audienceIds[$audienceClass])) {
            $audienceIds = $audienceIds[$audienceClass];
        }
        $audienceBwType = $this->I('audience_bw_list');
        
        if($audienceClass == 0) {
            $audienceIds = [];
            $bw = -1;
        } else if ($audienceBwType == 0) {
            $bw = 0;
        } else {
            $bw = 1;
        }
        $this->updateConditionBWListType($solutionId, $model, $bw);
        $where = ['solution_id'         => $solutionId, 
                  'is_whitelist'        => $bw];
        $this->update($model, $where);
        if ($bw == -1) return '';
        $where['audience_client_id']  = $audienceClass;
        $inputIds = implode($audienceIds, ',');
        $inputIds = explode(',', $inputIds);
        $inputIds = array_unique($inputIds);
        $ids = $this->getAll('AudienceType', ['audience_client' => $audienceClass]);
        $allowedIds = array_column($ids, 'id');
        $audienceIds = array_intersect($inputIds, $allowedIds);
        $audienceIds = implode(',', array_unique($audienceIds));
        $data = array_merge($where, ['audience_type_id' => $audienceIds]);
        return $this->save($model, $data);
    }

    public function saveConditionChannel() {
        $model = 'ConditionChannel';
        $solutionId = $this->I('solution_id');
        $channelIds = $this->I('youtu_channels');
        $bw = $this->getStatus('youtu_black_white_radio');
        if (!$channelIds) {
            $bw = -1;
        }
        $this->updateConditionBWListType($solutionId, $model, $bw);
        if ($bw == self::NOLIMIT) {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => 0];
        } else {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => $bw]; 
        }
        !$channelIds && $channelIds = [];
        $channelIds = implode(',', array_unique($channelIds));
        $where = ['solution_id' => $solutionId, 'is_whitelist' => $bw];
        $this->update($model, $where);
        if ($bw == self::NOLIMIT) return true;
        $data = array_merge($where, ['channel_id_list' => $channelIds]);
        return $this->save($model, $data);
    }

    public function saveConditionUrl() {
        $model = 'ConditionUrl';
        $solutionId = $this->I('solution_id');
        $urls = $this->I('urls');
        $bw = $this->getStatus('urls_black_white_radio');
        if (!$urls) {
            $bw = -1;
        }
        $this->updateConditionBWListType($solutionId, $model, $bw);
        if ($bw == self::NOLIMIT) {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => 0];
        } else {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => $bw]; 
        }
        !$urls && $urls = '';
        $where = ['solution_id' => $solutionId, 'is_whitelist' => $bw];
        $this->update($model, $where);
        if ($bw == self::NOLIMIT) return true;
        $data = array_merge($where, ['urls' => $urls]);
        return $this->save($model, $data);
    }

    public function saveConditionRetarget() {
        $retargetTypeId = $this->I('retarget_type_id');
        $tag = $this->I('tag');
        $solutionId = $this->I('solution_id');
        $model = 'ConditionRetarget';
        $bw =  $this->getStatus('retarget_launch');
        $this->updateConditionBWListType($solutionId, $model, $bw);
        if ($bw == self::NOLIMIT) {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => 0];
        } else {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => $bw]; 
        }
        $this->update($model, $where);
        if ($bw == self::NOLIMIT)
            return true;
        if ($retargetTypeId) {
            $data = array_merge($where, ['tag' => $tag, 'retarget_type_id' => $retargetTypeId]);
            return $this->save($model, $data);
        } 
        return true;
    }

    public function getWeatherIdLevel() {
        $subTypes = $this->getAll('WeatherType');
        $group = $this->arrayGroup($subTypes, 'pid', ['id']);
        return $group;
    }

    public function saveConditionIp() {
        $ips = $this->getIps();
        $this->saveConditionType('ConditionIp', 'ip', 'condition_ips', $ips);
    }

    public function getZones() {
        $geos = $this->I('geo');
        if (empty($geos)) $geos = [];
        return $geos;
    }

    // 定向值较多，自定义存取
    public function saveConditionZone() {
        $zones = $this->getZones();
        $model = 'ConditionZone';
        $solutionId = $this->S('solution_id');
        $bw = $this->getStatus('geo_black_white_radio');
        $this->updateConditionBWListType($solutionId, $model, $bw);

        if ($bw == self::NOLIMIT) {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => 0];
        } else {
            $where = ['solution_id' => $solutionId, 'is_whitelist' => $bw];
        }
        $this->update($model, $where);

        if ($bw == self::NOLIMIT) return true;
        $data = array_merge($where, ['zone_id'=>implode(',', $zones)]);
        $this->save($model, $data);
    }

    public function getDomains() {
        $brands = $this->I('brands');
        if (empty($brands) || !$brands[0]) return []; 
        $brandsList = trim(array_shift($brands), ',');
        $brands = explode(',', $brandsList);
        return $brands;
    }

    public function saveConditionBrandSafe() {
        $domains = $this->getDomains();
        $this->saveConditionType('ConditionBrand', 'brand', 'brands', $domains);
    }

    public function updateConditionBWListType($solutionId, $model, $bw) {
        $solution = $this->getOne('Solution', ['id' => $solutionId]);
        $maps = $this->mobileConditionIdNameMaps;
        if ($solution['type'] == 1) {
            $maps = $this->conditionIdNameMaps;
        }
        $conditionMaps = array_flip($maps);
        $conditionMaps = $maps;
        $type = $conditionMaps[$model];
        $data = [];
        if ($bw == self::NOLIMIT) {
            $data = ['use_whitelist' => 0, 'is_limit' => 0];
        } elseif ($bw == 0) {
            $data = ['use_whitelist' => $bw, 'is_limit' => 1];
        } elseif ($bw == 1) {
            $data = ['use_whitelist' => $bw];
        }
        $where = ['solution_id' => $solutionId, 'type_id' => $type];
        $condition = $this->getOne('Condition', $where);
        if ($condition) {
            $this->update('Condition', $where, $data);
        } else {
            $data['type_id'] = $type;
            $this->save('Condition', $data);
        }
    }

    public function saveConditionType($model, $status, $name, $values=[], $plus=[]) {
        $status .= '_black_white_radio';
        $status = $this->getStatus($status);
        //update all corresponding list
        $solutionId = $this->I('solution_id');
        $data['solution_id'] = $solutionId;

        if ($status == self::NOLIMIT) {
            $data['is_whitelist'] = 0;
        } else {
            $data['is_whitelist'] =  $status;
        }

        $updateData = array_merge($data, ['status'=>1]);
        $this->updateConditionBWListType($solutionId, $model, $status);
        $this->update($model, $updateData);
        if($status == self::NOLIMIT)
            return true;
        
        if (empty($values) && $name != 'adspace_type') $values = $this->I($name);
        
        if (!$values) return true;
        foreach ($values as $val) {
            if (is_array($val) && empty($val)) continue;
            else if (!$val) continue;
            $data = array_merge($data, $this->getConditionTypeFields($model, $val));
            //添加不需预先处理的数据
            $data = array_merge($data, $plus);
            $this->checkAndSave($model, $data);
            $this->update($model, $data, ['status'=>1]);
        }
    }

    public function getConditionTypeFields($model, $val) {
        $data = [];
        switch($model) {
            case 'ConditionAdspaceCategory':
                $data['adspace_category_id'] = $val;
                break;
            case 'ConditionLanguage':
                $lang = $this->getAll('Language', ['code'=>$val]);
                if (empty($lang)) {
                    $data = [];
                    break;
                }
                $lang = array_pop($lang);
                $data = ['language_id'  => $lang['id'],
                         'code'         => $val,
                         'language'     => $lang['language']];
                break;
            case 'ConditionAdspace':
                $data['adspace_id'] = $val;
                break;
            case 'ConditionIp':
                $data = $val;
                break;
            case 'ConditionZone':
                $data['zone_id'] = $val;
                break;
            case 'ConditionInstrument':
                $data['instrument_category_id'] = $val;  
                break;
            case 'ConditionBrand':
                $data['brand_safe'] = $val;
                break;
            case 'ConditionFrequency':
                $data['frequency'] = $val;
                break;
        }
        return $data;
    }

    protected function getStatus($key) {
        $listType = $this->I($key);
        $listType == 'no_limit' && $listType = -1;
        $listType == 'white' && $listType = 1;
        $listType == 'black' && $listType = 0;
        return $listType;
    }

    /**
     * calculate solution left days for live
     */
    public function getSolutionLeftDays($startDate, $endDate) {
        $today = date('Y-m-d');
        $past = F::dateDiff($startDate, $today);
        $future = F::dateDiff($today, $endDate);
        $life = F::dateDiff($startDate, $endDate);
        // 已经过期
        //if ($future == 0) return $future;
        // 未开时
        if ($life <= $future) return $life;
        // 已开始
        return $future;
    }

    /**
     * 获取当日 solution 预期执行小时数
     */
    public function getSolutionTodayActiveHours($solution) {
        $beg = strtotime($solution['start_date']);
        $end = strtotime($solution['end_date']);
        $today = strtotime(date('Y-m-d'));
        //未考试
        if ($beg > $today) return 0;
        //已经结束
        if ($end < $today) return 0;
        // 若今日不投放
        $today = date('w');
        $workingdays = explode(',', $solution['weekday_slot']);
        if (!in_array($today, $workingdays)) return 0;
        $pauseHours = $this->getSolutionPauseHour($solution['id']);
        $todayHours = [
            $today.'00', $today.'01', $today.'02', $today.'03', $today.'04', $today.'05',
            $today.'06', $today.'07', $today.'08', $today.'09', $today.'10', $today.'11',
            $today.'12', $today.'13', $today.'14', $today.'15', $today.'16', $today.'17',
            $today.'18', $today.'19', $today.'20', $today.'21', $today.'22', $today.'23',
        ];
        return count(array_diff($todayHours, $pauseHours));
    }

    /**
     * get solution current daily budget
     * TODO budget on hour
     */
    public function getSolutionDailyBudget($solutionId) {
        $todayBudget = 0;        
        $solution = $this->getOne('Solution', ['id' => $solutionId]);
        $balance = $this->getOne('SolutionBalance', ['solution_id' => $solutionId]);
        $beg = strtotime($solution['start_date']);
        $end = strtotime($solution['end_date']);
        $today = strtotime(date('Y-m-d'));
        $todayBudget = $balance['cost_today'];
        if ($balance['hours'] <= 0) {
            return $todayBudget;
        }
        if ($today < $beg) {
            return $todayBudget;
        }
        if ($today > $end) {
            return $todayBudget;
        }
        if ($today == $end) {
            return $todayBudget = $solution['budget'] - $balance['cost_from_log'];
        }

        $workingdays = explode(',', $solution['weekday_slot']);
        $w = date('w');
        if (!in_array($w, $workingdays)) {
            if($balance['cost_today']) $todayBudget = $balance['cost_today']; 
            return $todayBudget;
        }
        $tomorrowHour = date('Ymd00', strtotime ( "+1 day" ));   
        list($burnHours, $valid) = $this->calculateSolutionHours($solution, $tomorrowHour, $type = 'valid'); 
        if ($valid) $burnHours += 1;
        $todayBudget = $solution['budget'] - (($solution['budget'] - $balance['cost_before_edit'])/$balance['hours'])*$burnHours
                        - $balance['cost_from_log'];
        return round($todayBudget,2);
    }

    public function getAllSolutions() {
        $aIds = $this->getAIds();
        if (!$aIds) return [];
        $where = " advertiser_id in $aIds ";
        $solutionWhere = " advertiser_id in $aIds ";
        if ($this->I('campaign_id')) {
            $solutionWhere .= " and campaign_id = ".$this->I('campaign_id');
        }
        if ($solutionWhere) $solutionWhere .= ' and ';
        $type = ' type in (1,2) ';
        if (count(array_diff([1,2], $this->S('traffic_slot'))) == 2) {
            $type = ' type in (2) '; 
        } if (count(array_diff([3,4], $this->S('traffic_slot'))) == 2) {
            $type = ' type in (1) '; 
        }
        $solutionWhere .= $type;
        $solutions = $this->getAll('Solution', $solutionWhere . ' and status <> 4');
        $campaigns = $this->getAll('Campaign', $where);
        $creatives = $this->getAll('SolutionCreativeMap', $where." and status = 1 ");
        $creatives = $this->arrayGroup($creatives, 'solution_id');

        $report = new ReportService();
        $solutionReportData = $report->getSolutionReportData(false);
        $totalSolutionReportData = $report->getSolutionReportData(true);

        //$serv = new RedisCacheService();
        foreach ($solutions as $key => &$solution) {
            $solution['range_date'] = $solution['start_date'].' - '.$solution['end_date'];
            // 附加投放名称
            $solution['campaign'] = '';
            if ($solution['campaign_id']) {
                $solution['campaign'] = $this->t('solution', 'unknown');
                if (isset($campaigns[$solution['campaign_id']]))
                {
                    $solution['campaign'] = $campaigns[$solution['campaign_id']]['name'];
                }
            }
            // 附加 daily_budget
            $solution['daily_budget'] = 0;
            if ($solution['budget_flag'] == 1) {
                // $balance = $this->getOne('SolutionBalance', ['solution_id' => $solution['id']]);
                // $leftHour = $this->getSolutionTodayLeftHours($solution['id']);
                // $todayBudget = $balance['cost_today'] + $balance['balance'] * $leftHour;
                $solution['daily_budget'] = $this->getSolutionDailyBudget($solution['id']);
            }
            //附加 creative count
            $solutionId = $solution['id'];
            $solution['creativeCount'] = 0;
            if (isset($creatives[$solutionId])) {
                $solution['creativeCount'] = count($creatives[$solutionId]);
            }
            // attach report data
            $solutionReport = ['cost'       => 0.00,
                               'totalCost'  => 0.00,
                               'shows'      => 0,
                               'clicks'     => 0,
                               'clickRate'  => 0.00,
                               'cpm'        => 0.00,
                               'cpc'        => 0.00];
            if (in_array($solution['id'], array_keys($solutionReportData))) {
                $solutionReport = $solutionReportData[$solution['id']];
            }
            if (in_array($solution['id'], array_keys($totalSolutionReportData))) {
                $solutionReport['totalCost'] = $totalSolutionReportData[$solution['id']]['cost'];
            }
            $solution = array_merge($solution, $solutionReport);
            $solution['daily_rate'] = 0.00;
            $solution['total_rate'] = 0.00;
            if ($solution['daily_budget'] != 0) $solution['daily_rate'] = round($solution['cost']/$solution['daily_budget']*100, 2);
            if ($solution['budget'] != 0) $solution['total_rate'] = round($solution['totalCost']/$solution['budget']*100, 2);
        }
        return $this->fieldsMap($this->matrix, $solutions);
    }

    /**
     * 获取策略定向所使用的无限制和黑白名单列表
     */
    public function getConditionBWList($solutionId, $type='is_for_mobile') {
        $list = $this->getAll('Condition', ['solution_id' => $solutionId], 'type_id');
        $maps = $this->conditionIdNameMaps;
        if ($type == 'is_for_mobile' || $type == 2) {
            $maps = $this->mobileConditionIdNameMaps;
        }
        $conditionModel = array_flip($maps);
        $bwList = [];
        foreach ($list as $condition => $bw) {
            if ($bw['use_whitelist'] == 1) 
                $bwValue = 'white';
            else if ($bw['use_whitelist'] == 0 && $bw['is_limit'] == 0)
                $bwValue = 'no_limit';
            else if ($bw['use_whitelist'] == 0 && $bw['is_limit'] == 1)
                $bwValue = 'black';
            $conditionKey = $conditionModel[$condition];
            $bwList[$conditionKey] = $bwValue;
        }
        if (!isset($bwList['ConditionUrl']) && $type !='is_for_mobile') {
            $bwList['ConditionUrl'] = 'no_limit';
        }
        return $bwList;
    }
    
    /**
     * 取得系统定向的父子分类id集合
     */
    public function getInstrumentsMaps() {
        $instruments = $this->sort2LevelCates('Instrument', 'id');
        $datas = [];
        foreach ($instruments as $parent => $child) {
            foreach ($child as $instrument) {
                $datas[$parent][] = $instrument['id'];
            }
        }
        return $datas;
    }

    /**
     * 获取主要的字段
     * 根据当前的黑白名单获取相应的定向数据集
     *
     */
    public function getConditionsKey($solutionId, $type='is_for_mobile') {
        $bwList = $this->getConditionBWList($solutionId, $type);
        $conditions = $this->getSolutionConditions($solutionId, $type);
        $conditionKeys = [];
        foreach ($conditions as $model => $values) {
            $conditionKeys[$model] = [];
            if (!isset($bwList[$model])) $bw = 0;
            else if ($bwList[$model] == 'no_limit') $bw = 0;
            else if ($bwList[$model] == 'black') $bw = 0;
            else if ($bwList[$model] == 'white') $bw = 1;
            foreach ($values as $k => $v) {
                if ($v['is_whitelist'] == $bw) {
                    $conditionKey = $this->getConditionKeyValues($model, $v);
                    $conditionKeys[$model][] = $conditionKey;
                }
            }
            if (is_array($values) && count($values) == 0) {
                $conditionKeys[$model] = [];
            }
        }
        return $conditionKeys;
    }

    public function getConditionKeyValues($model, $v) {
        $fields = $this->conditionKeys[$model];
        if (count($fields) == 1) {
            return $v[$fields[0]];
        }
        $datas = [];
        foreach ($fields as $field) {
            $datas[$field] = $v[$field];
        }
        return $datas;
    }

    /**
     * 获取solution对应的定向值, 
     * 包括黑白名单所有的有效（status=1)值
     */
    public function getSolutionConditions($solutionId, $type='is_for_mobile')  {
        $conditions = []; 
        $maps = $this->conditionIdNameMaps;
        if ($type == 'is_for_mobile' || $type == 2) {
            $maps = $this->mobileConditionIdNameMaps;
        }
        $maps = array_flip($maps);
        $where = ['solution_id' => $solutionId, 'status' => 1];
        foreach ($maps as $typeId => $model) {
            $conditions[$model] = $this->getAll($model, $where);
        }
        return $conditions;
    }

    public function getSolutionCreatives($solutionId) {
        $cs = new CreativeService();
        $crts = $cs->getCreativesList($solutionId);
        return $crts;
    }

    /**
     * 设置策略竞价
     * 一经设置出价，该solution便处于可投放状态
     * 可投放状态的solution可以暂停，暂停的solution
     * 可以重新开始投放
     */
    public function readySolution($bid,$oldSolutionStatus=null) {
        // RTB
        $data['adspace_type'] = 1;
        $bidType = '2';
        if (isset($bid['bid_type'])) {
            $bidType = $bid['bid_type'];
        }
        $data['bid_type'] = $bidType;
        //if ($bid['fix_bid_price']) {
            //$data['fix_bid_price'] = $bid['fix_bid_price'];
        //}
        if ($bidType == 2) {
            //if ($bid['optimize_bid_cpm']) {
                //$data['optimize_bid_cpm'] = $bid['optimize_bid_cpm'];
                $data['optimize_bid_cpm'] = 0;
            //}
            if (isset($bid['optimize_bid_cpc'])) {
                $data['optimize_bid_cpc'] = $bid['optimize_bid_cpc'];
            }
        }
        if ($bidType == 3) {
            if ($bid['optimize_ctr_cpm']) {
                $data['optimize_ctr_cpm'] = $bid['optimize_ctr_cpm'];
            }
            if ($bid['optimize_ctr_target']) {
                $data['optimize_ctr_target'] = $bid['optimize_ctr_target'];
            }
        }
        $id = $this->S('solution_id');
        if(!empty($oldSolutionStatus)){
            $data['status'] = $oldSolutionStatus;
        } else {
            $data['status'] = 7;
        }
        $solution = $this->M('Solution', $id);
        $this->saveOn('Solution', ['id'=>$id], $data);
        $this->tryFreshOptPrice($solution);
    }

    public function updateSolutionNameOnSolutionCreativeMap($solution)
    {
        $name = $solution['name'];
        $id = $solution['id'];
        $maps = $this->getAll('SolutionCreativeMap', ['solution_id' => $id]);
        foreach ($maps as $map) {
            $this->saveOn('SolutionCreativeMap', ['id' => $map['id']], ['solution_name' => $name]);
        }
    }

    public function isBindCreative($solutionId){
        $bind = $this->getOne('SolutionCreativeMap',['solution_id'=>$solutionId,'status'=>'1']);
        $isBind = false;
        if($bind){
            $isBind = true;
        }
        return $isBind;
    }

    // try fresh opt_price table if solution bid_type = 2 and optimize_bid_cpc, optimize_bid_cpm changed
    public function tryFreshOptPrice($solution) {   
        $bidType = $solution['bid_type'];
        if ($bidType == 1) return ;
        if ($bidType == 2) {
            $price = $solution['optimize_bid_cpm'];
            $cpm = $solution['optimize_bid_cpm'];
            $cpc = $solution['optimize_bid_cpc'];
            if (!$cpc) $cpc = $this->I('optimize_bid_cpc');
            if (!$cpc) $cpc = 0;
            $sql = " update {{%opt_price}} set price = $price, opt_cpm = $cpm, opt_cpc = $cpc, status = 1 where solution_id = ${solution['id']}";
        }
        if ($bidType == 3) {
            $price = $solution['optimize_ctr_cpm'];
            $cpm = $solution['optimize_ctr_cpm'];
            $ctr = $solution['optimize_ctr_target'];
            $sql = " update {{%opt_price}} set price = $price, opt_cpm = $cpm, opt_ctr = $ctr, status = 1 where solution_id = ${solution['id']}";

        }
        return $this->executeSql($sql);
    }
    
    public function changeStatusToId($status) {
        switch ($status) {
            case 'pause':
                $statusId = 3;
                break;
            case 'resume':
                $statusId = 2;
                break;
            case 'nobind':
                $statusId = 7;
                break;
            default:
                $statusId = 2;
                break;
        }
        return $statusId;
    }

    public function updateSolutonStatus($solutionId, $type) {
        $statusId = $this->changeStatusToId($type);
        return $this->saveOn('Solution', ['id' => $solutionId], ['status' => $statusId]);
    }

    /**
     * solution 解绑 creative
     */
    public function unbindCreative($solutionId, $creativeId) {
        $solution = $this->M('Solution', $solutionId);  
        if (!$solution) {
            $this->NG($this->t('solution', 'Solution not found!'));
        }
        $where = [];
        $where['ad_network_id'] = $solution['ad_network_id'];
        $where['solution_id'] = $solutionId;
        $save = $this->saveOn('SolutionCreativeMap', ['creative_id' => $creativeId, 'solution_id' => $solutionId], ['status' => 0]);
        $isBind = $this->isBindCreative($solutionId);
        if(!$isBind){
            $this->updateSolutonStatus($solutionId,'nobind');
        }
        return $save;
    }

    /**
     * solution bind creative
     */
    public function bindCreative($solutionId, $creativeIds) {
        $solution = $this->M('Solution', $solutionId);  
        if (!$solution) {
            $this->NG($this->t('solution', 'Solution not found!'));
        }
        $where = [];
        $where['ad_network_id'] = $solution['ad_network_id'];
        $where['solution_id'] = $solutionId;
        $where['solution_name'] = $solution['name'];
        $where['advertiser_id'] = $solution['advertiser_id'];
        $where['status'] = 1;

        foreach ($creativeIds as $creativeId) {
            $creative = $this->M('Creative', $creativeId);
            if (!$creative) {
                $this->NG($this->t('solution', 'Creative not found!'));
            }
            $where['creative_id'] = $creative['id'];
            $where['creative_name'] = $creative['name'];
            if ($solution['advertiser_id'] != $creative['advertiser_id']) {
                $this->NG($this->t('solution', 'Solution and creative brand not agree!'));
            }
            $this->saveOn('SolutionCreativeMap', ['creative_id' => $creativeId, 'solution_id' => $solutionId], $where);
        }
        if($solution['status'] == '7'){
            $this->updateSolutonStatus($solutionId,'resume');
        }      
    }

    /**
     * 获取当前用户的所有creative, 
     * 后续需要根据 solution 进行 creative 过滤，比如尺寸
     * of no use yet
     */
    public function getUserCreatives($solution=[]) {
        $service = new CreativeService();
        return $creativeList = $service->getCreativesList();
    }

    /**
     * 获取 GEO rank city 集合
     */
    public function getRankCities() {
        $geo = new geo;
        $ranks = $geo->rankUp();
        $ranks = $this->arrayGroup($ranks, 'rank', ['cities']);
        foreach ($ranks as $rank => &$cities) {
            $cities = $this->arrayGroup($cities[0], 'id', ['id', 'name']);
            $cities = array_keys($cities);
        }
        $other = $geo->rankedOtherArea;
        $ranks[$other['rank']] = explode(',',$other['cities'][0]['id']);
        $main = $geo->rankedMainCity;
        $ranks[$main['rank']] = array_keys($this->arrayGroup($main['cities'], 'id', ['id']));
        return $ranks;
    }

    public function getCampaignAttributes($campaignId) {
        $Campaign = $this->M('Campaign', $campaignId);
        list($max, $min) = $this->getSolutionTotalBudgetRange($campaignId);
        $attr = [];
        if ($Campaign) {
            $attr['startDate'] = $Campaign['start_date'];
            $attr['endDate'] = $Campaign['end_date'];
            $attr['totalBudget'] = $max;
            $attr['minBudget'] = $min;
        }
        return $attr;
    }

    public function getRetargetSite($retargetTypeId=1) {
        $adId = $this->S('advertiser_id');
        $where = ['retarget_type_id' => $retargetTypeId, 'advertiser_id' => $adId, 'status' => 1];
        $sites = $this->getAll('RetargetSite', $where); 
        return $sites;
    }

    /**
     * 新建或者更新策略时，获取策略的最大最小总预算界线
     */
    public function getSolutionTotalBudgetRange($campaignId=0, $solutionId=0) {
        //默认应该该当前账户余额   
        if (!$solutionId) $solutionId = $this->S('solution_id');
        $max = 9999999999;
        $min = 0;
        if (!$campaignId) return [$max, $min];
        //若提供campaignId
        // $max is
        $bidCampaign = $this->M('Campaign', $campaignId);
        if (!$bidCampaign) return [$max, $min];
        $max = $bidCampaign['total_budget'];

        // get campaign exists solutions
        $where['campaign_id'] = $campaignId;
        $allCampaignSolutions = $this->getAll('Solution', $where);
        // get all but not bid solution budget sum
        // 减去其它solution预算
        foreach ($allCampaignSolutions as $id => $solution) {
            if ($id != $solutionId) {
                $max -= $solution['budget'];
            } 
        }
        // 否则若为更新，需要减去该solution已经花费的预算
        if ($solutionId) {
            //$Rpt = new ReportService();
            //$min = $Rpt->getSolutionCosts($solutionId, true);
            //$serv = new RedisCacheService();
            //$min += $serv->getCacheSolutionBudget($solutionId);
            $min += 0;
            $balance = $this->getOne('SolutionBalance', ['solution_id' => $solutionId]);
            if ($balance) {
                $min += $balance['cost_from_log'];
                $min += $balance['cost_today'];
                $min += $balance['balance'];
                //$solution = $this->getOne('Solution', ['id' => $solutionId]);
                //$nowHour = date('YmdH', time());
                //list($burnHours, $valid) = $this->calculateSolutionHours($solution, $nowHour, $type = 'invalid');
                // if ($valid) {
                //     $min += $balance['balance'];
                // }
            }
        }
        //if switched campaign left budget less than solution already cost
        if ($max < 0) $max = 0;
        return [round($max, 2), round($min, 2)];
    }

    public function getCampaignId() {
        $campaigns = $this->getAll('Campaign', ['advertiser_id'=>$this->S('advertiser_id')]);
        $camId = $this->I('campaignId');
        !$camId && $camId = $this->I('id');

        if (!$camId && is_array($campaigns)) {
            $camId = array_values($campaigns);
            if (count($camId)) {
                $camId = $camId[0]['id'];
            }
        }
        return $camId;
    }

    public function getAdNetwork($where = []) {
        $matrix = ['id' => 'id',    
                   'name' => ['name', '$v=="vamaker" && $v=\Yii::t("common", "ValueMaker"); return $v;'],
                  ];
        if($where) {
            $condition = [];
            foreach ($where as $key => $value) {
                $condition[] = "$key = $value";
            }
            $where = ' where '.implode(' and ', $condition);
            $sql = "select id, name from {{%ad_network}}".$where;
            $AdNetworks = $this->queryOne($sql);
            $AdNetworks['name'] == "vamaker" && $AdNetworks['name'] = \Yii::t("common", "ValueMaker");
        } else {
            $where = '';
            $sql = "select id, name from {{%ad_network}}".$where;
            $AdNetworks = $this->queryAll($sql, $matrix);
        }
        return $AdNetworks;
    }

    public function updateSolutionTime($solutionId) {
        $condition = ['id' => $solutionId];
        $data['last_update'] = date('Y-m-d H:i:s');
        $this->update('Solution', $condition, $data);
    }

    public function getAdspaceCategory() {
        $adNetworkId = $this->S('ad_network_id');
        $adNetworkId == 3 && $adNetworkId = 1;
        !$adNetworkId && $adNetworkId = 1;
        $adspaceCategory = $this->sort2LevelCates('AdspaceCategory', 'name', $adNetworkId);
        return $adspaceCategory;
    }

    public function getAllSolutionAuditList() {
        // 暂时展示所有品牌下的未审核策略
        $solutions = $this->getAll('Solution', ['solution_audit' => 0]);
        $campaigns = $this->getAll('Campaign');
        foreach ($solutions as &$solution) {
            $solution['range_date'] = $solution['start_date'].' - '.$solution['end_date'];
            // 附加投放名称
            $solution['campaign'] = '';
            if ($solution['campaign_id']) {
                $solution['campaign'] = \Yii::t("common", "unknown");
                if (isset($campaigns[$solution['campaign_id']]))
                {
                    $solution['campaign'] = $campaigns[$solution['campaign_id']]['name'];
                }
            }
        }
        return $this->fieldsMap($this->auditMatrix, $solutions);
    }

    public function auditSolution() {
        $solutionId = $this->I('solution_id');
        $data = ['id' => $solutionId,'solution_audit' => 1];
        $result = $this->saveOn('Solution', ['id' => $solutionId], $data);
        if(is_array($result)) {
            $this->OK($this->t('solution', 'Solution passed!'));
        } else {
            $this->NG($this->t('solution', 'Solution not passed!'));
        }
    }
    
    public function getAudienceCategory($pid=0) {
        $audienceCates = $this->getAll('AudienceType', ['parent_id' => $pid]);
        $cates = [];
        foreach ($audienceCates as $id => $cate) {
            // self gender
            if ($id == '405' && $cate['title'] == '性别') continue;
            // miaozhen gender
            if ($id == '424' && $cate['title'] == '性别') continue;
            // admaster gender
            if ($id == '429' && $cate['title'] == '性别') continue;
            $pilotCates = [
                'id'        => $id,
                'label'     => $cate['title'],
                'load_on_demand' => false,
            ];
            $subCates = $this->getOne('AudienceType', ['parent_id' => $id]);
            if ($subCates) {
                $pilotCates['load_on_demand'] = true;
            }
            $cates[] = $pilotCates;
        }
        return json_encode($cates, JSON_UNESCAPED_UNICODE);
    }

    public function getAudienceGenderCategory($client=1) {
        $where = ['audience_client' => $client, 'parent_id' => 0, 'title' => '性别'];
        $genderCate = $this->getOne('AudienceCategory', $where);
        $subs = $this->getAll('AudienceCategory', ['parent_id' => $genderCate['id']]);
        $genderCate = [ 'id'            => $genderCate['id'], 
                        'label'         => $genderCate['title'], 
                        'load_on_demand' => true,
                        'children'      => []];
        foreach ($subs as $id => $cate) {
            $pilotCates = [
                'id'        => $id,
                'label'     => $cate['title'],
                'load_on_demand' => false,
            ];
            $genderCate['children'][] = $pilotCates;
        }
        return json_encode($genderCate, JSON_UNESCAPED_UNICODE);
    }

    public function getAudienceWithClient() {
        $selfAudienceIdentity = $this->getAudienceCategory(1);
        $selfAudienceInteresting = $this->getAudienceCategory(66);
        $selfAudienceTrend = $this->getAudienceCategory(184);
        $mzAudienceAge = $this->getAudienceCategory(409);
        $mzAudienceHobby = $this->getAudienceCategory(418);
        $mzAudienceGender = $this->getAudienceCategory(424);
        $adPeople = $this->getAudienceCategory(428);
        $adMedia = $this->getAudienceCategory(783);
        $adIndustry = $this->getAudienceCategory(831);
        $adDevice = $this->getAudienceCategory(978);
        $allSelfAudience= $this->getAllAudienceCategories(1);
        $allMzAudience= $this->getAllAudienceCategories(2);
        $allAdAudience= $this->getAllAudienceCategories(3);
        return ['self'  => [$selfAudienceIdentity, $selfAudienceInteresting, $selfAudienceTrend],
                'mz'    => [$mzAudienceAge, $mzAudienceHobby, $mzAudienceGender],
                'ad'    => [$adPeople, $adMedia, $adIndustry, $adDevice],
                'all'   => [$allSelfAudience, $allMzAudience, $allAdAudience]
                        ];
    }

    public function getAllAudienceCategories($client=1) {
        $all = $this->getAll('AudienceType', ['audience_client' => $client]);
        $identity = [];
        $hobby = [];
        $trend = [];
        $cates = [];
        foreach ($all as $cate) {
            $id = $cate['id'];
            $pid = $cate['parent_id'];
            $record = [];
            if (in_array($pid, $identity) || $pid == 1) {
                $identity[] = $id;
            } 
            if (in_array($pid, $hobby) || $pid == 66) {
                $hobby[] = $id;
            } 
            if (in_array($pid, $trend) || $pid == 184) {
                $trend[] = $id;
            } 
            $cates[$id] = $cate['title'];
        }
        return $cates;
    }

    public function checkSolutionDate() {
        list($beg, $end) = explode(' - ', $this->I('range_date'));
        $beg = strtotime($beg);
        $today = strtotime(date('Y-m-d'));
        if ($today > $beg) {
            $this->NG($this->t('solution', 'Solution start date is not later than today!'));
        }
    }
    public function getYouTuChannels($media) {
        $sql = "select id, media_id, media_pid, media_name, channel_name
                from {{%media_channel}}
                where media_pid = '0' and media_name like '$media%'";
        $channels = $this->query($sql);
        $channels = $this->arrayGroup($channels, 'media_name');
        return $channels;
    }

    public function getPmps() {
        $pmps = $this->getAll('FlowPmp', ['status' => 2]);
        return $pmps;
    }

    public function getPmpPrice($guid) {
        $pmps = $this->getAll('FlowPmp', ['status' => 2, 'guid' => $guid]);
        $pmp = array_pop($pmps);
        if ($pmp) {
            $this->jsonReturn(['status' => 'success', 'message' => $this->t('solution', 'PMP price loaded!'), 'bid' => $pmp['pmp_price']]);
        }
        $this->jsonReturn(['status' => 'failed', 'message' => $this->t('solution', 'PMP not found!')]);
    }

    public function getPmpAdspaces($pmpGuid) {
        if ($pmpGuid == 0) return [];
        $where = ['flow_pmp_guid'   => $pmpGuid,
                  'status'          => 1];
        $adspaces = $this->getAll('FlowPmpAdspaceMap', $where);
        $pmpAds = [];
        foreach ($adspaces as $adspace) {
            $table = 'Adspace';
            if ($adspace['adspace_class_id'] == 2) {
                $table = 'VideoAdspace';
            }
            if ($adspace['adspace_class_id'] == 3) {
                $table = 'MobileAdspace';
            }
            if ($adspace['adspace_class_id'] == 4) {
                $table = 'MobileVideoAdspace';
            }
            $where = ['ad_network_adspace_id' => $adspace['adspace_guid']];
            $dspAdspace = $this->getOne($table, $where);
            if ($dspAdspace) $pmpAds[] = $dspAdspace;
        }
        return $pmpAds;
    }

    public function savePmpAdspaces($oldSolution, $solution) {
        $solutionId = (int)$solution['id'];
        $where = ['solution_id' => $solutionId, 'type_id' => 8];
        if (!$oldSolution) {
            if ($solution['is_pmp'] == 1) {
                $typeData = ['solution_id' => $solutionId, 'type_id' => 8, 'use_whitelist' => 1];
                $this->update('Condition', $where, $typeData);
                $this->update('ConditionAdspace', ['solution_id' => $solutionId]);
                $this->touchPmpAdspaces($solution);
            }
        } else {
            if ($oldSolution['is_pmp'] == $solution['is_pmp'] && $oldSolution['pmp_guid'] != $solution['pmp_guid']) {
                $typeData = ['solution_id' => $solutionId, 'type_id' => 8, 'use_whitelist' => 1];
                $this->update('Condition', $where, $typeData);
                $this->update('ConditionAdspace', ['solution_id' => $solutionId]);
                $this->touchPmpAdspaces($solution);
            } else if ($oldSolution['is_pmp'] != $solution['is_pmp'] && $solution['is_pmp'] == 1) {
                $typeData = ['solution_id' => $solutionId, 'type_id' => 8, 'use_whitelist' => 1];
                $this->update('Condition', $where, $typeData);
                $this->update('ConditionAdspace', ['solution_id' => $solutionId]);
                $this->touchPmpAdspaces($solution);
            } else if ($oldSolution['is_pmp'] != $solution['is_pmp'] && $solution['is_pmp'] == 0) {
                $typeData = ['solution_id' => $solutionId, 'type_id' => 8, 'use_whitelist' => 0, 'is_limit' => 0];
                $this->update('Condition', $where, $typeData);
                $this->update('ConditionAdspace', ['solution_id' => $solutionId]);
            }          
        }

    }

    public function touchPmpAdspaces($solution) {
        $pmpAdspaces = $this->getPmpAdspaces($solution['pmp_guid']);
        $pmpAdspaces = array_column($pmpAdspaces, 'ad_network_adspace_id');
        $where = 'ad_network_adspace_id in ('.implode(',', $pmpAdspaces).')';
        $adspaces = $this->getAll('Adspace', $where);
        $vadspaces = $this->getAll('VideoAdspace', $where);
        $madspaces = $this->getAll('MobileAdspace', $where);
        $mvadspaces = $this->getAll('MobileVideoAdspace', $where);    
        $adspaces = array_merge($adspaces, $vadspaces, $madspaces, $mvadspaces);
        $adspaces = array_column($adspaces, 'id');
        $data['solution_id'] = $solution['id'];
        $data['is_whitelist'] = 1;
        foreach ($adspaces as $adspace) {
            $data['adspace_id'] = $adspace;
            $where = $data;
            $data['status'] = 1;
            $this->saveOn('ConditionAdspace', $where, $data);
        }
    }

    public function getAllAdNetWork() {
        $adnetworks = $this->S('adnetwork_slot');
        $adnetworkWhere = ' id in ('.implode(',', $adnetworks).')';
        if (in_array('0', $adnetworks)) {
            $adnetworkWhere = '';
        }

        $datas = $this->getAll('AdNetwork', $adnetworkWhere);
        $matrix = [
            'id'        => 'id',
            'channel'   => ['name', '$v=="vamaker"&&$v=\Yii::t("resource", "ValueMaker");
                                     return $v;'],
        ];
        $datas = $this->fieldsMap($matrix, $datas);
        return $datas;
    }

    public function getAdNetworkAdspace() {
        $adNetworkIds = $this->I('adnetwork');
        $search = $this->I('search');
        $type = $this->I('type');

        $on = "ad_network_id in ($adNetworkIds)";
        if (!$adNetworkIds) {
            $ret = [];
            $this->jsonReturn(['items' => $ret]);
        }
        !$on && $on.= ' status = 0 ';
        $on && $on .= ' and status = 0 ';
        if ($search) {
            $on .= ' and name like "%'.$search.'%" ';
        }

        if (!in_array(0, $this->s('media_slot'))) {
            $ids = implode(',', $this->s('media_slot'));   
            $medias = $this->getAll('AdnetworkMedia', "id in ($ids)");
            $medias = array_column($medias, 'media_id');
            $mediaIds = array_values($medias);
            if ($mediaIds) {
                $adspaceMediaIds = implode(',', $mediaIds);
                $on .= " and media_id in ($adspaceMediaIds) ";
            }
        }
        $adspaces = [];
        $vadspaces = [];
        $madspaces = [];
        $mvadspaces = [];
        if ($type == 'pc') {
            $sql = "select CONCAT(ad_network_id, '_', id) id, name text from {{%adspace}} where ". $on;
            $adspaces = $this->query($sql);
            $sql = "select CONCAT(ad_network_id, '_', id) id, name text from {{%video_adspace}} where ". $on;
            $vadspaces = $this->query($sql);
        }
        if ($type == 'mobile') {
            $sql = "select CONCAT(ad_network_id, '_', id) id, name text from {{%mobile_adspace}} where ". $on;
            $madspaces = $this->query($sql);
            $sql = "select CONCAT(ad_network_id, '_', id) id, name text from {{%mobile_video_adspace}} where ". $on;
            $mvadspaces = $this->query($sql);
        }
        $ret = [];
        if (in_array(1, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $adspaces);
        }
        if (in_array(2, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $vadspaces); 
        }
        if (in_array(3, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $madspaces);
        }
        if (in_array(4, $this->s('traffic_slot'))) {
            $ret = array_merge($ret, $mvadspaces);
        }

        $this->jsonReturn(['items' => $ret]);
    }

    public function getSolutionCampaigns($solutionId = '') {
        $advertiserId = $this->S('advertiser_id');
        if ($solutionId) { 
            $solution = $this->M('Solution', $solutionId);
            $advertiserId = $solution['advertiser_id'];
        }
        return $this->getAll('Campaign', ['advertiser_id'=>$advertiserId]);
    }

    public function getSolutionTargetedCityReqs($solutionId='', $cities=[])
    {
        $targetedAdspace = $this->getSolutionTargetedAdspace($solutionId);
        $adGuids = $this->getAdspaceGuids($targetedAdspace);
        $requestByCity = $this->getRequestOnAdspace($adGuids, $cities);
        return $requestByCity;
    }

    public function getAdspaceGuids($targetedAdspace)
    {
        $guids = $this->getColumn('MobileAdspace', ['id' => $targetedAdspace], ['ad_network_adspace_id']);
        return array_column($guids, 'ad_network_adspace_id');
    }

    public function getRequestOnAdspace($adspaceIds=[], $cityList=[])
    {
        $yday = date('Ymd', strtotime('-1 day'));
        $on = "where logdate_id = $yday";
        if ($adspaceIds) {
            $on .= ' and  adspaceid in (' . implode(',', $adspaceIds) . ')';
        }
        $geo = new geo();
        $cities = $geo->getAllCityName();
        $cities = array_intersect_key($cities, array_flip($cityList));
        $cities = array_values($cities);
        //$dummyReqs = array_pad([], count($cities), 0);
        //$cityReqs = array_combine($cities, $dummyReqs);
        if ($cities) {
            $on .= " and cn_city in ('" . implode("','", $cities) . "')";
        }
        $sql = "select sum(shows) reqs
                from {{%rpt_app_location_hour}} as r
                $on";
        $reqs = $this->query($sql);
        $reqs = $reqs[0]['reqs'];
        if (!$reqs) return 0;
        return $reqs;
    }

    public function getSolutionTargetedAdspace($solutionId='')
    {
        $allAdspaceIds = $this->getAdspaceIdsByAppId();
        $key = 'ConditionAdspace';
        if ($solutionId) {
            $conditions = $this->getConditionsKey($solutionId);
            $adspaceIds = $conditions[$key];
            $bwList = $this->getConditionBWList($solutionId);
            $bwList = $bwList[$key];
            if ($bwList == 'white') 
                return array_intersect($allAdspaceIds, $adspaceIds);
            if ($bwList == 'black') 
                return array_diff($allAdspaceIds, $adspaceIds);
        }
        return $allAdspaceIds;
    }

    public function getAdspaceIdsByAppId($appId=[])
    {
        $on = [];
        if ($appId) {
            if (!is_array($appId)) $appId = [$appId];
            $on = ['app_id' => $appId];
        }
        $maps = $this->getAll('ResourceAppAdspaceMap', $on);
        $adspaceIds = array_column($maps, 'adspace_id');
        return $adspaceIds;
    }

    public function getSolutionPackPriceRange($solutionId)
    {
        $adGuids = $this->getSolutionTargetedAdspace($solutionId);
        //$adGuids = $this->getAdspaceGuids($targetedAdspace);
        $dummy = [0.01, 0.02];
        if (!$adGuids) return $dummy;
        $appIds = Raamap::find()->select(['app_id'])
            ->where(['status' => 1, 'adspace_id' => $adGuids])
            ->asArray()->all();
        if (!$appIds) return $dummy;
        $appIds = array_column($appIds, 'app_id');
        $packIds = ResourceApp::find()->select(['resource_pack_id'])
            ->where(['status' => 1, 'id' => $appIds])
            ->asArray()->all();
        if (!$packIds) return $dummy;
        $packIds = array_column($packIds, 'resource_pack_id');
        $prices = ResourcePack::find()
            ->select(['min(floor_price) floor', 'max(ceiling_price) ceiling'])
            ->where(['id' => $packIds])
            ->asArray()->one();
        if ($prices) {
            return [$prices['floor'], $prices['ceiling']];
        }
        return $dummy;
    }

    public function getIsTargetedAllApps($targetedApps)
    {
        $apps = ResourceApp::find()->select(['id'])
            ->where(['status' => 1, 'type' => 1])->asArray()->all(); 
        $waps = ResourceApp::find()->select(['id'])
            ->where(['status' => 1, 'type' => 2])->asArray()->all(); 
        $ids = array_column($targetedApps, 'app_id'); 
        $appIds = array_column($apps, 'id');
        $wapIds = array_column($waps, 'id');
        $appDiff = array_diff($appIds, $ids);
        $wapDiff = array_diff($wapIds, $ids);
        if (($appIds && !$appDiff) || ($wapIds && !$wapDiff)) return true;
        return false;
    }
}// end of service declaration
